#include "grid.h"
#include "vecmath.h"
#include "factories.h"
#include "textrenderer.h"
#include <QtCore>

Grid::Grid(float start, float unitLength)
: start(start), unitLength(unitLength), compiledGrid(NULL), mode(GL_LINES), modified(true)
{
}

Grid::~Grid()
{
    if (compiledGrid)
        delete compiledGrid;
}

void Grid::drawVBOWithCamera(Camera& camera)
{
    // Intersect the X-axis with the left and right camera planes
    Vec4f leftComp, rightComp;
    Vec3f positionScale = {1, 1, 1};
    camera.getLeftPlaneComponents(leftComp, positionScale, 0, 1);
    camera.getRightPlaneComponents(rightComp, positionScale, 0, 1);
    long startUnit = (long)floorf(-leftComp[3] / (leftComp[0] * unitLength));
    long endUnit = (long)ceilf(-rightComp[3] / (rightComp[0] * unitLength));
    
    // Get the pixel per unit ratio
    long units = endUnit - startUnit;
    if (units < 0)
        units = -units;
    if (units == 0)
        units = 1;
    float pixelsPerUnit = camera.getWidth() / (float)units;
    
    // Derive the correct grid spacing in units for the given ratio
    long minPixelsPerUnit = 8;
    long unitsPerGrid = 1;
    while ((long)(pixelsPerUnit * (float)unitsPerGrid) < minPixelsPerUnit)
        unitsPerGrid *= 10;
    
    // Generate the grid only when required
    if (modified || startUnit != lastStartUnit || endUnit != lastEndUnit || units != lastUnits || unitsPerGrid != lastUnitsPerGrid || start != lastStart || unitLength != lastUnitLength) {
		modified = false;
        lastStartUnit = startUnit;
        lastEndUnit = endUnit;
        lastUnits = units;
        lastUnitsPerGrid = unitsPerGrid;
        lastStart = start;
        lastUnitLength = unitLength;
        
        renderGrid = false;
        generateGrid(units, startUnit, endUnit, unitsPerGrid, pixelsPerUnit);
    }
    
    // Draw the compiled grid
	if (compiledGrid)
		compiledGrid->oneShotDraw(mode);
}

void Grid::drawVAWithCamera(Camera& camera)
{
	TextRenderer& textRenderer = TextRenderer::instance();
	textRenderer.setInputAlignment(TextRenderer::Center, TextRenderer::Center);
	textRenderer.setOutputAlignment(TextRenderer::Center, TextRenderer::Center);
	
	glPushMatrix();
	
	glColor4ubv(ColorFactory::instance().getGridLabelColor());
	
	long currentUnit = lastFirstSecondaryGridUnit;
	long unitStep = lastUnitsPerGrid * 10;
	glTranslatef((float)currentUnit * unitLength, horizontalPosition, 0.0f);
	
	char number[256];
	while (currentUnit <= lastEndUnit) {
		sprintf(number, "%ld", currentUnit);
		BoundingBox bb = textRenderer.getWorldBoundingBoxWithCamera(number, camera);
		
		// Pretransform the quad to keep it facing towards the camera
		glPushMatrix();
		camera.setBillBoard();
		
		// Setup the color since the color array is not used
		Color4b color;
		ColorFactory::instance().getGridBackgroundColor(color);
		
		// Create an index array for a closed line strip
		GLubyte lineStripIndices[] = {0, 1, 2, 3, 0};
		GLsizei stride = sizeof(PackedVecColor);
		
		VertexArray* rect = ObjectFactory::instance().createRectangle(bb.min, bb.max, color);
		glVertexPointer(3, GL_FLOAT, stride, (GLfloat *)rect->getVertices());
		glColor4ubv(ColorFactory::instance().getGridBackgroundColor());
		glDrawElements(GL_QUADS, sizeof(lineStripIndices) - 1, GL_UNSIGNED_BYTE, lineStripIndices);
		glColor4ubv(ColorFactory::instance().getGridBackgroundBorderColor());
		glDrawElements(GL_LINE_STRIP, sizeof(lineStripIndices), GL_UNSIGNED_BYTE, lineStripIndices);
		delete rect;
		
		glPopMatrix();
		
		currentUnit += unitStep;
		glTranslatef((float)unitStep * unitLength, 0.0f, 0.0f);
	}
	
	glPopMatrix();
}

void Grid::drawTextWithCamera(Camera& camera)
{
	TextRenderer& textRenderer = TextRenderer::instance();
	textRenderer.setInputAlignment(TextRenderer::Center, TextRenderer::Center);
	textRenderer.setOutputAlignment(TextRenderer::Center, TextRenderer::Center);
	
	glPushMatrix();
	
	glColor4ubv(ColorFactory::instance().getGridLabelColor());
	
	long currentUnit = lastFirstSecondaryGridUnit;
	long unitStep = lastUnitsPerGrid * 10;
	glTranslatef((float)currentUnit * unitLength, horizontalPosition, 0.0f);
	
	char number[256];
	while (currentUnit <= lastEndUnit) {
		glPushMatrix();
		camera.setBillBoard();
		sprintf(number, "%ld", currentUnit);
		textRenderer.drawWithCamera(number, camera);
		glPopMatrix();
		currentUnit += unitStep;
		glTranslatef((float)unitStep * unitLength, 0.0f, 0.0f);
	}
	
	glPopMatrix();
}

void Grid::generateGrid(long units, long startUnit, long endUnit, long unitsPerGrid, float pixelsPerUnit)
{
    renderGrid = true;
    
    // The first gridline has to be on a multiple of unitsPerGrid
    long gridStartUnit = startUnit;
    if (gridStartUnit < start)
        gridStartUnit = start;
    else {
        int offset = gridStartUnit % unitsPerGrid;
        if (offset > 0)
            gridStartUnit -= offset;
    }
    
    unsigned gridLineCount = 1 + units / unitsPerGrid;
    long nextUnitsPerGrid = unitsPerGrid * 10;
    
    // Create the grid
    Vec3f top = {0, 1000, 0};
    Vec3f bottom = {0, -1000, 0};
    Color4b color, nextColor;
    SetColor4b(color, 0.7f, 0.7f, 0.7f, 1.0f);
    SetColor4b(nextColor, 0.9f, 0.9f, 0.9f, 1.0f);
    
    VertexArray* gridVertexArray = new VertexArray(gridLineCount * 2);
    PackedVecColor* vertices = gridVertexArray->getVertices();
    long counter = 0;
	bool firstSecondaryGridReached = false;
    while (gridLineCount--) {
        long currentUnit = gridStartUnit + counter * unitsPerGrid;
        bool useNextColor = (currentUnit % nextUnitsPerGrid == 0);
		
		if (!firstSecondaryGridReached && useNextColor) {
			firstSecondaryGridReached = true;
			lastFirstSecondaryGridUnit = currentUnit;
		}
		
        float currentPos = (float)currentUnit * unitLength;
        top[0] = currentPos;
        bottom[0] = currentPos;
        counter++;
        
        SetVec3f(vertices->v, top);
        SetColor4b(vertices->c, useNextColor ? nextColor : color);
        vertices++;
        
        SetVec3f(vertices->v, bottom);
        SetColor4b(vertices->c, useNextColor ? nextColor : color);
        vertices++;
    }

    // Setup the compiled grid for rendering
    if (!compiledGrid) {
        compiledGrid = new VertexBufferObject(gridVertexArray);
        compiledGrid->generate();
    }
    else
        compiledGrid->setVertexArray(gridVertexArray);
    compiledGrid->send();
}


// ElementsGrid implementation

float ElementsGrid::elementsGridMinimalWidth = 1.0f;

ElementsGrid::ElementsGrid(float start, float unitLength)
: Grid(start, unitLength)
{
    mode = GL_QUADS;
    boundingBox.min[1] = -0.5;
    boundingBox.max[1] = 0.5;
}

void ElementsGrid::drawVBOWithCamera(Camera& camera)
{
    glPushMatrix();
    glScalef(scaling[0], scaling[1], scaling[2]);
    glTranslatef(position[0], position[1], position[2]);
    Grid::drawVBOWithCamera(camera);
    glPopMatrix();
}

void ElementsGrid::drawVAWithCamera(Camera& camera)
{
    if (!renderGrid)
        return;
    
    glPushMatrix();
    glScalef(scaling[0], scaling[1], scaling[2]);
    glTranslatef(position[0], position[1], position[2]);
    
    // Setup the color since the color array is not used
    glColor4ubv(ColorFactory::instance().getSelectionColor());
    
    // Create an index array for a closed line strip
    GLubyte lineStripIndices[] = {0, 1, 2, 3, 0, 2};
    GLsizei stride = sizeof(PackedVecColor);
    
    QSetIterator<Element*> selectionIter(selectedElements);
    while (selectionIter.hasNext()) {
        Element* element = selectionIter.next();
        PackedVecColor* elementVertices = getElementVertices(element);
        if (elementVertices) {
            glVertexPointer(3, GL_FLOAT, stride, (GLfloat *)elementVertices);
            glDrawElements(GL_LINE_STRIP, sizeof(lineStripIndices), GL_UNSIGNED_BYTE, lineStripIndices);
        }
    }
    
    glPopMatrix();
}

void ElementsGrid::drawTextWithCamera(Camera& camera)
{
    if (!renderGrid)
        return;
	
    TextRenderer& renderer = TextRenderer::instance();
	renderer.setInputAlignment(TextRenderer::Left, TextRenderer::Center);
    
    glPushMatrix();
    glTranslatef(position[0], position[1], position[2]);
    glScalef(scaling[0], scaling[1], scaling[2]);
    for (int index = getStartIndex(); index < getEndIndex(); index++) {
		
		if (index < elements.size()) {
			Element* element = elements.at(index);
			
			glPushMatrix();
			
			// The final scaling has to be undone, since scaling is being handled by the text renderer
			glTranslatef((float)index, 0.25, 0); // set the input alignment at the bottom
			glScalef(1/scaling[0], 1/scaling[1], 1/scaling[2]);
			renderer.setOutputAlignment(TextRenderer::Center, TextRenderer::Center);
			renderer.drawWithCamera(element->getDescription().toStdString().c_str(), camera, scaling[1], scaling[0]);
			
			glPopMatrix();
			glPushMatrix();
			
			glTranslatef((float)index, -0.25, 0);
			glScalef(1/scaling[0], 1/scaling[1], 1/scaling[2]);
			renderer.setOutputAlignment(TextRenderer::Center, TextRenderer::Center);
			renderer.drawWithCamera(element->getSecondaryDescription().toStdString().c_str(), camera, scaling[1], scaling[0]);
			
			glPopMatrix();
		}
    }
    
    glPopMatrix();
}

void ElementsGrid::addElement(Element* element)
{
    elements.append(element);
    ((Event*)element)->index = elements.size() - 1;
}

QVector<Element*> ElementsGrid::pickElementsWithRay(const Ray& ray) const
{
    QVector<Element*> pickedElements;
    
    // Avoid explosions with rays parallel with the horizontal axis
    if (ray.direction[2] == 0)
        return pickedElements;
    
    Ray containerRelativeRay = getContainerRelativeRay(ray);
    
    // Calculate the horizontal position of the ray when Z is zero
    Vec3f intersection;
    containerRelativeRay.getPoint(intersection, 0);
    
    // Only pick if we are in the bounding box horizontally
    if (intersection[1] < boundingBox.min[1] || boundingBox.max[1] < intersection[1])
        return pickedElements;
    
    // The X position gives a direct indication of the picked event
    float floorX = floorf(intersection[0]);
    if (floorX < 0.0f)
        return pickedElements;
    
    int eventIndex = (int)floorX;
    if (eventIndex < elements.size())
        pickedElements.append(elements.at(eventIndex));
    
    return pickedElements;
}

long ElementsGrid::getStartIndex() const
{
    return (lastStartUnit < 0 ? 0 : lastStartUnit);
}

long ElementsGrid::getEndIndex() const
{
    return getStartIndex() + lastUnits;
}

PackedVecColor* ElementsGrid::getElementVertices(Element* element)
{
    Event* event = static_cast<Event*>(element);
    long startIndex = lastStartUnit;
    if (lastStartUnit < 0)
        startIndex = 0;
    long endIndex = startIndex + lastUnits;
    if ((long)event->index < startIndex || (long)event->index >= endIndex)
        return NULL;
    
    if (!compiledGrid)
        return NULL;

    if (lastStartUnit < 0)
        return compiledGrid->getVertexArray()->getVertices() + (((long)event->index) * 4);
    else
        return compiledGrid->getVertexArray()->getVertices() + (((long)event->index - lastStartUnit) * 4);
}

void ElementsGrid::generateGrid(long units, long startUnit, long endUnit, long unitsPerGrid, float pixelsPerUnit)
{
    VertexArray* gridVertexArray = NULL;
    
    // Only render if one or more pixels can be used per rendered event
    if (pixelsPerUnit >= elementsGridMinimalWidth) 
    {
        renderGrid = true;
        Vec3f pos = {0, 0.5, 0};
        
        if (startUnit >= elements.size())
            startUnit = elements.size();
        if ((startUnit + units) > elements.size())
            units = elements.size() - startUnit;
        
        gridVertexArray = new VertexArray(units * 4);
        PackedVecColor* vertices = gridVertexArray->getVertices();
        long counter = 0;
        if (startUnit < 0)
            startUnit = 0;
        while (units--) 
        {
            long currentUnit = startUnit + counter;// * unitsPerGrid;
            float currentPos = (float)currentUnit;// * unitLength;
            counter++;
            
            Element* element = elements.at(currentUnit);
            
            Color4b color;
            element->getFinalColor(color);
            
            pos[0] = currentPos + 1.0f;// + unitLength;
            pos[1] = 0.5;
            SetVec3f(vertices->v, pos);
            SetColor4b(vertices->c, color);
            vertices++;
            
            pos[0] = currentPos;
            SetVec3f(vertices->v, pos);
            SetColor4b(vertices->c, color);
            vertices++;
            
            pos[1] = -0.5;
            SetVec3f(vertices->v, pos);
            SetColor4b(vertices->c, color);
            vertices++;
            
            pos[0] = currentPos + 1.0f;// + unitLength;
            SetVec3f(vertices->v, pos);
            SetColor4b(vertices->c, color);
            vertices++;
        }
        
        // Setup the grid for rendering
        if (!compiledGrid)
        {
            compiledGrid = new VertexBufferObject(gridVertexArray);
            compiledGrid->generate();
        }
        else
        {
            compiledGrid->setVertexArray(gridVertexArray);
        }
        compiledGrid->send(false);
    }
}


// ElementsDoubleList implementation

ElementsDoubleList::ElementsDoubleList()
: compiledList(NULL), padding(1.0f), leftWidth(0.0f), rightWidth(0.0f)
{
}

ElementsDoubleList::~ElementsDoubleList()
{
	if (compiledList)
		delete compiledList;
}

void ElementsDoubleList::setupLayoutWithCamera(Camera& camera)
{
	TextRenderer& textRenderer = TextRenderer::instance();
	
	// Get the world space height for the text
	Vec3f worldBaseVec, worldHeightVec, worldHeightDiff;
	Vec2f screenBaseVec = {0, 0};
	Vec2f screenHeightVec = {0, textRenderer.getMaxPixelHeight()};
	camera.getWorldPoint(worldBaseVec, screenBaseVec, 0);
	camera.getWorldPoint(worldHeightVec, screenHeightVec, 0);
	SubtractVec3f(worldHeightDiff, worldHeightVec, worldBaseVec);
	float worldTextHeight = sqrtf(InnerProductVec3f(worldHeightDiff, worldHeightDiff));
	float worldTextWidth = worldTextHeight * 8.0f;
	
	// Get the world space height available for the list
	Vec4f topPlaneComp, bottomPlaneComp;
	Vec3f positionScale = {1, 1, 1};
	camera.getTopPlaneComponents(topPlaneComp, positionScale, 1, 1);
	camera.getBottomPlaneComponents(bottomPlaneComp, positionScale, 1, 1);
	float worldHeight = (-topPlaneComp[3] / topPlaneComp[1]) - (-bottomPlaneComp[3] / bottomPlaneComp[1]);
	
	// Get the world space width for positioning
	Vec4f leftPlaneComp, rightPlaneComp;
	camera.getLeftPlaneComponents(leftPlaneComp, positionScale, 1, 1);
	camera.getRightPlaneComponents(rightPlaneComp, positionScale, 1, 1);
	float worldWidth = (-rightPlaneComp[3] / rightPlaneComp[0]) - (-leftPlaneComp[3] / leftPlaneComp[0]);
	
	// See how many text boxes fit on the screen and adjust the padding for optimal filling and centering
	float textBoxPadding = worldTextHeight / 2.0f;
	float maxTextBoxes = ceilf((worldHeight - textBoxPadding) / (worldTextHeight + textBoxPadding));
	textBoxPadding = (worldHeight - (maxTextBoxes * worldTextHeight)) / (maxTextBoxes + 1.0f);
	
	// Save the calculated valued for later usage
	lastWorldHeight = worldHeight;
	lastWorldTextWidth = worldTextWidth;
	lastWorldTextHeight = worldTextHeight;
	lastWorldTextBoxPadding = textBoxPadding;
	lastMaxTextBoxes = maxTextBoxes;
	lastWorldLeftTextCenter = (-worldWidth + worldTextWidth + worldTextHeight) / 2.0f;
	lastWorldRightTextCenter = (worldWidth - worldTextWidth - worldTextHeight) / 2.0f;
	leftWidth = lastWorldLeftTextCenter + lastWorldTextWidth / 2.0f + worldTextHeight / 2.0f;
	rightWidth = lastWorldRightTextCenter - lastWorldTextWidth / 2.0f - worldTextHeight / 2.0f;
	
	Vec3f drawpos;
	camera.getPosition(drawpos);
	
	lastWorldLeftBoundary = -worldWidth / 2.0f + worldTextWidth + worldTextHeight / 2.0f + drawpos[0];
	lastWorldRightBoundary = worldWidth / 2.0f - worldTextWidth - worldTextHeight / 2.0f + drawpos[0];
}

void ElementsDoubleList::drawVBOWithCamera(Camera& camera)
{
	TextRenderer& textRenderer = TextRenderer::instance();
	
	// Get the world space height for the text
	Vec3f worldBaseVec, worldHeightVec, worldHeightDiff;
	Vec2f screenBaseVec = {0, 0};
	Vec2f screenHeightVec = {0, textRenderer.getMaxPixelHeight()};
	camera.getWorldPoint(worldBaseVec, screenBaseVec, 0);
	camera.getWorldPoint(worldHeightVec, screenHeightVec, 0);
	SubtractVec3f(worldHeightDiff, worldHeightVec, worldBaseVec);
	float worldTextHeight = sqrtf(InnerProductVec3f(worldHeightDiff, worldHeightDiff));
	float worldTextWidth = worldTextHeight * 8.0f;
	
	// Get the world space height available for the list
	Vec4f topPlaneComp, bottomPlaneComp;
	Vec3f positionScale = {1, 1, 1};
	camera.getTopPlaneComponents(topPlaneComp, positionScale, 1, 1);
	camera.getBottomPlaneComponents(bottomPlaneComp, positionScale, 1, 1);
	float worldHeight = (-topPlaneComp[3] / topPlaneComp[1]) - (-bottomPlaneComp[3] / bottomPlaneComp[1]);
	
	// Get the world space width for positioning
	Vec4f leftPlaneComp, rightPlaneComp;
	camera.getLeftPlaneComponents(leftPlaneComp, positionScale, 1, 1);
	camera.getRightPlaneComponents(rightPlaneComp, positionScale, 1, 1);
	float worldWidth = (-rightPlaneComp[3] / rightPlaneComp[0]) - (-leftPlaneComp[3] / leftPlaneComp[0]);
	
	// See how many text boxes fit on the screen and adjust the padding for optimal filling and centering
	float textBoxPadding = worldTextHeight / 2.0f;
	float maxTextBoxes = ceilf((worldHeight - textBoxPadding) / (worldTextHeight + textBoxPadding));
	textBoxPadding = (worldHeight - (maxTextBoxes * worldTextHeight)) / (maxTextBoxes + 1.0f);
	
	// Save the calculated values for later usage
	lastWorldHeight = worldHeight;
	lastWorldTextWidth = worldTextWidth;
	lastWorldTextHeight = worldTextHeight;
	lastWorldTextBoxPadding = textBoxPadding;
	lastMaxTextBoxes = maxTextBoxes;
	lastWorldLeftTextCenter = (-worldWidth + worldTextWidth + worldTextHeight) / 2.0f;
	lastWorldRightTextCenter = (worldWidth - worldTextWidth - worldTextHeight) / 2.0f;
	leftWidth = lastWorldLeftTextCenter + lastWorldTextWidth / 2.0f;
	rightWidth = lastWorldRightTextCenter - lastWorldTextWidth / 2.0f;
	
	// Create a vertex array to receive the data and create the boxes
	VertexArray* listVertexArray = new VertexArray(8 * (unsigned)maxTextBoxes);
	PackedVecColor* vertices = listVertexArray->getVertices();
	int count = (int)maxTextBoxes;
	Vec3f pos = {-worldWidth / 2.0f + worldTextWidth + worldTextHeight / 2.0f, worldHeight / 2.0f - textBoxPadding, 0.0f};
	while (count--) {
		Color4b color;
		if (count < leftList.size())
			leftList.at(leftList.size() - (count + 1))->getFinalColor(color);
		else
			ColorFactory::instance().getListColor(color);
		
		// Interpolate the alpha component of the color
		if (maxTextBoxes > 1.0f)
			color[3] = 255.0f * InvLerp(0.25, 1.0, (float)count / (maxTextBoxes - 1.0f));
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[0] -= worldTextWidth;
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[1] -= worldTextHeight;
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[0] += worldTextWidth;
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[1] -= textBoxPadding;
	}
	
	count = (int)maxTextBoxes;
	pos[0] = worldWidth / 2.0f - worldTextHeight / 2.0f;
	pos[1] = worldHeight / 2.0f - textBoxPadding;
	pos[2] = 0.0f;
	while (count--) {
		Color4b color;
		int listIndex = ((int)maxTextBoxes - (count + 1));
		if (listIndex < rightList.size())
			rightList.at(listIndex)->getFinalColor(color);
		else
			ColorFactory::instance().getListColor(color);
		
		// Interpolate the alpha component of the color
		if (maxTextBoxes > 1.0f)
			color[3] = 255.0f * Lerp(0.25, 1.0, (float)count / (maxTextBoxes - 1.0f));
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[0] -= worldTextWidth;
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[1] -= worldTextHeight;
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[0] += worldTextWidth;
		
		SetVec3f(vertices->v, pos);
		SetColor4b(vertices->c, color);
		vertices++;
		pos[1] -= textBoxPadding;
	}
	
    // Setup the list for rendering
    if (!compiledList) {
        compiledList = new VertexBufferObject(listVertexArray);
        compiledList->generate();
    }
    else
        compiledList->setVertexArray(listVertexArray);
    compiledList->send(false);
	
	Vec3f drawpos;
	camera.getPosition(drawpos);
	
	lastWorldLeftBoundary = -worldWidth / 2.0f + worldTextWidth + worldTextHeight / 2.0f + drawpos[0];
	lastWorldRightBoundary = worldWidth / 2.0f - worldTextWidth - worldTextHeight / 2.0f + drawpos[0];
	
	glPushMatrix();
	glTranslatef(drawpos[0], drawpos[1], 0.0);
	compiledList->oneShotDraw(GL_QUADS);
	glPopMatrix();
}

void ElementsDoubleList::drawVAWithCamera(Camera& camera)
{
    if (!compiledList)
        return;
    
	Vec3f pos;
	camera.getPosition(pos);
	
    glPushMatrix();
    
    // Setup the transformation for the container
    glTranslatef(pos[0], pos[1], 0.0);
    
    // Setup the color since the color array is not used
    glColor4ubv(ColorFactory::instance().getSelectionColor());
    
    // Create an index array for a closed line strip
    GLubyte lineStripIndices[] = {0, 1, 2, 3, 0, 2, 3, 1};
    GLsizei stride = sizeof(PackedVecColor);
    
	if (leftHitsFirst) {
		glVertexPointer(3, GL_FLOAT, stride, (GLfloat *)getFirstLeftVertices());
		glDrawElements(GL_LINE_STRIP, sizeof(lineStripIndices), GL_UNSIGNED_BYTE, lineStripIndices);
	}
	
	if (rightHitsFirst) {
		glVertexPointer(3, GL_FLOAT, stride, (GLfloat *)getFirstRightVertices());
		glDrawElements(GL_LINE_STRIP, sizeof(lineStripIndices), GL_UNSIGNED_BYTE, lineStripIndices);
	}
    
    glPopMatrix();
}

void ElementsDoubleList::drawTextWithCamera(Camera& camera)
{
	TextRenderer& textRenderer = TextRenderer::instance();
	textRenderer.setInputAlignment(TextRenderer::Center, TextRenderer::Top);
	textRenderer.setOutputAlignment(TextRenderer::Center, TextRenderer::Center);
	
	Vec3f pos;
	camera.getPosition(pos);
	
	glPushMatrix();
	glTranslatef(pos[0], pos[1], 0.0);
	
	glPushMatrix();
	glTranslatef(lastWorldLeftTextCenter, lastWorldHeight / 2.0f - lastWorldTextBoxPadding + (lastMaxTextBoxes - 1.0f) * (-(lastWorldTextHeight + lastWorldTextBoxPadding)), 0.0f);
	
	int count = (int)lastMaxTextBoxes;
	if (count > leftList.size())
		count = leftList.size();
	while (count--) {
		// leftList.at(count)
		textRenderer.drawWithCamera(leftList.at(count)->getDescription().toStdString().c_str(), camera, lastWorldTextHeight, lastWorldTextWidth);
		glTranslatef(0.0f, (lastWorldTextHeight + lastWorldTextBoxPadding), 0.0f);
	}
	glPopMatrix();
	
	glPushMatrix();
	glTranslatef(lastWorldRightTextCenter, lastWorldHeight / 2.0f - lastWorldTextBoxPadding, 0.0f);
	
	count = (int)lastMaxTextBoxes;
	if (count > rightList.size())
		count = rightList.size();
	while (count--) {
		// rightList.at(rightList.size() - (count + 1))
		textRenderer.drawWithCamera(rightList.at(rightList.size() - (count + 1))->getDescription().toStdString().c_str(), camera, lastWorldTextHeight, lastWorldTextWidth);
		glTranslatef(0.0f, -(lastWorldTextHeight + lastWorldTextBoxPadding), 0.0f);
	}
	glPopMatrix();
	
	glPopMatrix();
}

const PackedVecColor* ElementsDoubleList::getFirstLeftVertices() const
{
	if (!compiledList)
		return NULL;
	
	VertexArray *vertexArray = compiledList->getVertexArray();
	return (vertexArray->getVertices() + (vertexArray->getVerticesSize() / 2 - 4));
}

const PackedVecColor* ElementsDoubleList::getFirstRightVertices() const
{
	if (!compiledList)
		return NULL;
	
	VertexArray *vertexArray = compiledList->getVertexArray();
	return (vertexArray->getVertices() + (vertexArray->getVerticesSize() / 2));
}

bool ElementsDoubleList::hit(const Ray& ray) const
{
	Vec3f hitPoint;
	ray.getPoint(hitPoint, 0.0f);
	return (hitPoint[0] <= lastWorldLeftBoundary) || (hitPoint[0] >= lastWorldRightBoundary);
}

Ray ElementsDoubleList::getElementsDoubleListRelativeRay(const Ray& ray) const
{
    Ray elementsDoubleListRelativeRay(ray);
	elementsDoubleListRelativeRay.origin[1] = 0.0f;
    return elementsDoubleListRelativeRay;
}

Element* ElementsDoubleList::pickElementWithRay(const Ray& ray) const
{
	Element* hittedElement = NULL;
	
	Vec3f hitPoint;
	getElementsDoubleListRelativeRay(ray).getPoint(hitPoint, 0.0f);
	
	// Are we hitting the left or right list
	if (hitPoint[0] <= lastWorldLeftBoundary) {
		float base = -(hitPoint[1] - (lastWorldHeight / 2.0f) + lastWorldTextBoxPadding);
		float row = base / (lastWorldTextHeight + lastWorldTextBoxPadding);
		float rowBase = base - floorf(row) * (lastWorldTextHeight + lastWorldTextBoxPadding);
		if (rowBase <= lastWorldTextHeight) {
			// The box at row 'row' was hitted, get the corresponding element
			int rowIndex = (int)floorf(row) - ((int)lastMaxTextBoxes - leftList.size());
			if (rowIndex < leftList.size())
      {
        if (rowIndex < 0)
        {
          qDebug() << "pickElementWithRay() : rowIndex < 0";
        }
        else
        {
          hittedElement = leftList.at(rowIndex);
        }
      }
    }
	}
	else if (hitPoint[0] >= lastWorldRightBoundary) {
		float base = -(hitPoint[1] - (lastWorldHeight / 2.0f) + lastWorldTextBoxPadding);
		float row = base / (lastWorldTextHeight + lastWorldTextBoxPadding);
		float rowBase = base - floorf(row) * (lastWorldTextHeight + lastWorldTextBoxPadding);
		if (rowBase <= lastWorldTextHeight) {
			// The box at row 'row' was hitted, get the corresponding element
			int rowIndex = (int)floorf(row);
			if (rowIndex < rightList.size())
      {
        if (rowIndex < 0)
        {
          qDebug() << "pickElementWithRay() : rowIndex < 0";
        }
        else
        {
          hittedElement = rightList.at(rowIndex);
        }
      }
		}
	}
	
	return hittedElement;
}
